#ifndef VECTOR_HPP
#define VECTOR_HPP

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <math.h>
#include <stdio.h>

	//-------------------------------------------------------------------------
	class Vector {
	public:
		// Type definition	
		typedef float Vector2[2];
		typedef float Vector3[3];
		typedef float Vector4[4];
		
		// Vector 2 methods
		static inline void Init2(float *v, float x, float y);
		static inline void Zero2(float *v);
		static inline void Add2(float *dst, const float *v1, const float *v2);
		static inline void Sub2(float *dst, const float *v1, const float *v2);
		static inline void Mul2(float *dst, const float *v1, const float *v2);
		static inline void Div2(float *dst, const float *v1, const float *v2);
		static inline void Copy2(float *dst, const float *v);
		static inline float Dot2(const float *u, const float *v);
		static inline float Cross2(const float *u, const float *v);
		static inline void Swap2(float *u, float *v);
		static inline float SquareLength2(const float *v);
		static inline float Length2(const float *v);
		static inline void ConstMul2(float *dst, float k, const float *v);
		static inline void Norm2(float *dst, const float *v);
		static inline void Opposite2(float *dst, const float *v);
		
		// Vector 3 methods
		static inline void Init3(float *v, const float x, const float y, const float z);
		static inline void Zero3(float *v);
		static inline void Copy3(float *dst, const float *v);
		static inline void Negative3(float *dst, const float *v);
		static inline float Dot3(const float *v1, const float *v2);
		static inline float SquareLength3(const float *v);
		static inline float Length3(const float *v);
		static inline void Cross3(float *dst, const float *v1, const float *v2);
		static inline void Sub3(float *dst, const float *v1, const float *v2);
		static inline void Add3(float *dst, const float *v1, const float *v2);
		static inline void Mul3(float *dst, const float *v1, const float *v2);
		static inline void Div3(float *dst, const float *v1, const float *v2);
		static inline void AddConstMul3(float *dst, const float *p, float k, const float *v);
		static inline void ConstMul3(float *dst, float k, const float *v);
		static inline void Norm3(float *dst, const float *v);
		static inline void Mirror3(float *dst, float *n, const float *v);
		static inline void Inverse3(float *dst, const float *v);
		static inline void Opposite3(float *dst, const float *v);
		static inline void Lerp3(float *dst, float k, const float *v0, const float *v1);
		static inline int Max3(const float *vec);
		static inline int Min3(const float *vec);
		static inline void Print3(const float *vec);
		
		// Vector 4 methods
		static inline void Init4(float *v, float x, float y, float z, float w);		
		static inline void InitVector4(float *v, float *v3);
		static inline void InitPoint4(float *v, float *v3);
		static inline void Zero4(float *v);
		static inline void Copy4(float *dst, const float *v);
		static inline void Add4(float *dst, const float *v1, const float *v2);
		static inline void Sub4(float *dst, const float *v1, const float *v2);
		static inline void Mul4(float *dst, const float *v1, const float *v2);
		static inline void Div4(float *dst, const float *v1, const float *v2);
		static inline void ConstMul4(float *dst, const float k, const float *v);
		static inline void AddConstMul4( float *dst, const float *p, float k, const float *v);
		static inline float Dot4(const float *v1, const float *v2);
		static inline float SquareLength4(const float *v0);
		static inline float Length4(const float *v0);
		static inline void Norm4(float *dst, const float *v);
	};
	//-------------------------------------------------------------------------
	inline void Vector::Init2(float *v, float x, float y) {
		v[0]= x;
		v[1]= y;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Zero2(float *v)
	{
		v[0]= 0.f;
		v[1]= 0.f;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Add2(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] + v2[0];
		dst[1]= v1[1] + v2[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Sub2(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] - v2[0];
		dst[1]= v1[1] - v2[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Mul2(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] * v2[0];
		dst[1]= v1[1] * v2[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Div2(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] / v2[0];
		dst[1]= v1[1] / v2[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Copy2(float *dst, const float *v)
	{
		dst[0]= v[0];
		dst[1]= v[1];
	}
	//-------------------------------------------------------------------------
	inline float Vector::Dot2(const float *u, const float *v)
	{
		return u[0] * v[0] + u[1] * v[1];
	}
	//-------------------------------------------------------------------------
	inline float Vector::Cross2(const float *u, const float *v)
	{
		return u[0] * v[1] - u[1] * v[0];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Swap2(float *u, float *v)
	{
		float t[2];
		Copy2(t, u);
		Copy2(u, v);
		Copy2(v, t);
	}
	//-------------------------------------------------------------------------
	inline float Vector::SquareLength2(const float *v)
	{
		return Dot2(v, v);
	}
	//-------------------------------------------------------------------------
	inline float Vector::Length2(const float *v)
	{
		return sqrtf(Dot2(v, v));
	}
	//-------------------------------------------------------------------------
	inline void Vector::ConstMul2(float *dst, float k, const float *v)
	{
		dst[0]= k*v[0];
		dst[1]= k*v[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Norm2(float *dst, const float *v)
	{
		float d = 1.f / Length2(v);
		ConstMul2(dst, d, v);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Opposite2(float *dst, const float *v)
	{
		dst[0] = -v[0];
		dst[1] = -v[1];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Init4(float *v, float x, float y, float z, float w)
	{
		v[0]= x;
		v[1]= y;
		v[2]= z;
		v[3]= w;
	}
	//-------------------------------------------------------------------------
	inline void Vector::InitVector4(float *v, float *v3)
	{
		Init4(v, v3[0], v3[1], v3[2], 0.f);
	}
	//-------------------------------------------------------------------------
	inline void Vector::InitPoint4(float *v, float *v3)
	{
		Init4(v, v3[0], v3[1], v3[2], 1.f);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Zero4(float *v)
	{
		v[0]= 0.f;
		v[1]= 0.f;
		v[2]= 0.f;
		v[3]= 0.f;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Copy4(float *dst, const float *v)
	{
		dst[0]= v[0];
		dst[1]= v[1];
		dst[2]= v[2];
		dst[3]= v[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Add4(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] + v2[0];
		dst[1]= v1[1] + v2[1];
		dst[2]= v1[2] + v2[2];
		dst[3]= v1[3] + v2[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Sub4(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] - v2[0];
		dst[1]= v1[1] - v2[1];
		dst[2]= v1[2] - v2[2];
		dst[3]= v1[3] - v2[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Mul4(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] * v2[0];
		dst[1]= v1[1] * v2[1];
		dst[2]= v1[2] * v2[2];
		dst[3]= v1[3] * v2[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Div4(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] / v2[0];
		dst[1]= v1[1] / v2[1];
		dst[2]= v1[2] / v2[2];
		dst[3]= v1[3] / v2[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::ConstMul4(float *dst, const float k, const float *v)
	{
		dst[0]= (k)*v[0];
		dst[1]= (k)*v[1];
		dst[2]= (k)*v[2];
		dst[3]= (k)*v[3];
	}
	//-------------------------------------------------------------------------
	inline void Vector::AddConstMul4( float *dst, const float *p, float k, const float *v)
	{
		dst[0]= (p)[0] + (k)*v[0];
		dst[1]= (p)[1] + (k)*v[1];
		dst[2]= (p)[2] + (k)*v[2];
		dst[3]= (p)[3] + (k)*v[3];
	}
	//-------------------------------------------------------------------------
	inline float Vector::Dot4(const float *v1, const float *v2)
	{
		return v1[0]*v2[0]
			+ v1[1]*v2[1]
			+ v1[2]*v2[2]
			+ v1[3]*v2[3];
	}
	//-------------------------------------------------------------------------
	inline float Vector::SquareLength4(const float *v0)
	{
		return Dot4(v0, v0);
	}
	//-------------------------------------------------------------------------
	inline float Vector::Length4(const float *v0)
	{
		return sqrtf(SquareLength4(v0));
	}
	//-------------------------------------------------------------------------
	inline void Vector::Norm4(float *dst, const float *v)
	{
		float d = 1.f / Length4(v);
		ConstMul3(dst, d, v);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Init3(float *v, const float x, const float y, const float z)
	{
		v[0]= x;
		v[1]= y;
		v[2]= z;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Zero3(float *v)
	{
		v[0]= 0.f;
		v[1]= 0.f;
		v[2]= 0.f;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Copy3(float *dst, const float *v)
	{
		dst[0]= v[0];
		dst[1]= v[1];
		dst[2]= v[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Negative3(float *dst, const float *v)
	{
		dst[0]= -v[0];
		dst[1]= -v[1];
		dst[2]= -v[2];
	}
	//-------------------------------------------------------------------------
	inline float Vector::Dot3(const float *v1, const float *v2)
	{
		return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
	}
	//-------------------------------------------------------------------------
	inline float Vector::SquareLength3(const float *v)
	{
		return Dot3(v, v);
	}
	//-------------------------------------------------------------------------
	inline float Vector::Length3(const float *v)
	{
		return sqrtf(Dot3(v, v));
	}
	//-------------------------------------------------------------------------
	inline void Vector::Cross3(float *dst, const float *v1, const float *v2)
	{
		float tmp[3];
		tmp[0]= v1[1]*v2[2] - v1[2]*v2[1];
		tmp[1]= v1[2]*v2[0] - v1[0]*v2[2];
		tmp[2]= v1[0]*v2[1] - v1[1]*v2[0];
		Copy3(dst, tmp);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Sub3(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] - v2[0];
		dst[1]= v1[1] - v2[1];
		dst[2]= v1[2] - v2[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Add3(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] + v2[0];
		dst[1]= v1[1] + v2[1];
		dst[2]= v1[2] + v2[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Mul3(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] * v2[0];
		dst[1]= v1[1] * v2[1];
		dst[2]= v1[2] * v2[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Div3(float *dst, const float *v1, const float *v2)
	{
		dst[0]= v1[0] / v2[0];
		dst[1]= v1[1] / v2[1];
		dst[2]= v1[2] / v2[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::AddConstMul3(float *dst, const float *p, float k, const float *v)
	{
		dst[0]= (p)[0] + (k)*v[0];
		dst[1]= (p)[1] + (k)*v[1];
		dst[2]= (p)[2] + (k)*v[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::ConstMul3(float *dst, float k, const float *v)
	{
		dst[0]= k*v[0];
		dst[1]= k*v[1];
		dst[2]= k*v[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Norm3(float *dst, const float *v)
	{
		float d = 1.f / Length3(v);
		ConstMul3(dst, d, v);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Mirror3(float *dst, float *n, const float *v)
	{
		float d= 2.f * Dot3(n, v);
		dst[0]= d*n[0] - v[0];
		dst[1]= d*n[1] - v[1];
		dst[2]= d*n[2] - v[2];
		Norm3(dst, dst);
	}
	//-------------------------------------------------------------------------
	inline void Vector::Inverse3(float *dst, const float *v)
	{
		dst[0] = 1.f / v[0];
		dst[1] = 1.f / v[1];
		dst[2] = 1.f / v[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Opposite3(float *dst, const float *v)
	{
		dst[0] = -v[0];
		dst[1] = -v[1];
		dst[2] = -v[2];
	}
	//-------------------------------------------------------------------------
	inline void Vector::Lerp3(float *dst, float k, const float *v0, const float *v1)
	{
		dst[0] = v0[0] + (k)*(v1[0] - v0[0]);
		dst[1] = v0[1] + (k)*(v1[1] - v0[1]);
		dst[2] = v0[2] + (k)*(v1[2] - v0[2]);
	}
	//-------------------------------------------------------------------------
	inline int Vector::Max3(const float *vec)
	{
		if(fabs(vec[0]) >= fabs(vec[1])
		&& fabs(vec[0]) >= fabs(vec[2]))
			return 0;
		else if(fabs(vec[1]) >= fabs(vec[0])
			&& fabs(vec[1]) >= fabs(vec[2]))
			return 1;
		else
			return 2;
	}
	//-------------------------------------------------------------------------
	inline int Vector::Min3(const float *vec)
	{
		if(fabs(vec[0]) <= fabs(vec[1])
		&& fabs(vec[0]) <= fabs(vec[2]))
			return 0;
		else if(fabs(vec[1]) <= fabs(vec[0])
			&& fabs(vec[1]) <= fabs(vec[2]))
			return 1;
		else
			return 2;
	}
	//-------------------------------------------------------------------------
	inline void Vector::Print3(const float *vec)
	{
		printf("[%f %f %f]\n", vec[0], vec[1], vec[2]);
	}
	
#endif // VECTOR_HPP
